package com.ts.api.aspect;

import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.dataformat.JsonUtils;
import com.gitee.apanlh.util.date.DateUtils;
import com.gitee.apanlh.util.id.IdUtils;
import com.gitee.apanlh.util.net.addr.IpUtils;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.valid.ValidParam;
import com.gitee.apanlh.web.model.vo.RequestVO;
import com.gitee.apanlh.web.util.ServletUtils;
import com.ts.api.aspect.handler.ApiLogAspectHandler;
import com.ts.api.common.constant.api.ApiEnum;
import com.ts.api.common.context.ApiContext;
import com.ts.api.common.entity.ApiParameter;
import com.ts.api.exp.ApiMsgException;
import com.ts.api.module.api.entity.po.ApiLogPO;
import com.ts.api.task.log.ApiLogExecute;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class ApiLogReceiveAspect {

    @Autowired
    private ApiLogExecute apiLogExecute;
    @Autowired
    private ApiLogAspectHandler apiLogAspectHandler;

    @Before("execution(*  com.ts.api.module.api.controller.ApiMsgReceiveController.*(..))")
    public void before(JoinPoint joinPoint) {
        ApiLogPO logPO = new ApiLogPO();
        logPO.setCreateTime(DateUtils.currentTime());
        ApiContext.REQ_ID.set(IdUtils.snowFlakeIdStr(2L, 1L));
        ApiContext.LOG_OBJECT.set(logPO);
        ApiContext.TIME_TIME.set(System.currentTimeMillis());

        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
            if (arg instanceof String apiClientId) {
                ApiContext.CLIENT_ID.set(apiClientId);
            }
        }
        ApiContext.PARAMETER.set(ApiParameter.create());
    }

    /**
     *  拦截异常操作
     *
     *  @param joinPoint 切点
     *  @param e 异常
     */
    @AfterThrowing(value = "execution(*  com.ts.api.module.api.controller.ApiMsgReceiveController.*(..))", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        handleLog(joinPoint, e, null);
    }

    /**
     *  处理完请求后执行
     *
     *  @param joinPoint 切点
     */
    @AfterReturning(pointcut = "execution(*  com.ts.api.module.api.controller.ApiMsgReceiveController.*(..))", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, null, result);
    }

    protected void handleLog(final JoinPoint joinPoint, final Throwable e, Object jsonResult) {
        try {
            ApiLogPO logPO = ApiContext.LOG_OBJECT.get();

            Object[] args = joinPoint.getArgs();
            String apiClientId = null;
            RequestVO requestVO = null;
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                if (Eq.object(arg.getClass(), RequestVO.class)) {
                    requestVO = ((RequestVO) args[i]);
                }
                if (Eq.object(arg.getClass(), String.class)) {
                    apiClientId = ((String) args[i]);
                }
            }

            // *========数据库日志=========*//
            logPO.setEventStatus(1);
            logPO.setEventContent("返回成功");
            logPO.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
            logPO.setApiAccessProviderType(0);

            //  首次记录id
            if (ValidParam.isEmpty(logPO.getApiClientId())) {
                logPO.setApiClientId(apiClientId);
            }
            if (ValidParam.isEmpty(logPO.getEventType())) {
                logPO.setEventType(ApiContext.SERVICE_CODE.get());
            }

            if (e != null) {
                //  默认设置失败
                logPO.setEventStatus(0);

                if (e instanceof ApiMsgException) {
                    ApiMsgException apiExp = (ApiMsgException) e;
                    Integer code = apiExp.getApiCode();
                    String msg = apiExp.getApiMsg();

                    StringBuilder messageBuilder = StringUtils.createBuilder(e.getMessage()).append("\n");
                    //  监听器特殊处理，增加额外流程异常信息
                    if (ValidParam.isNotNull(apiExp.getException())) {
                        Exception eventInnerExp = apiExp.getException();
                        messageBuilder.append(eventInnerExp).append("\n");
                        StackTraceElement[] stackTrace = eventInnerExp.getStackTrace();
                        for (StackTraceElement stackTraceElement : stackTrace) {
                            messageBuilder.append("\t" + stackTraceElement).append("\n");
                        }
                    }

                    logPO.setEventCode(ClassConvertUtils.toStr(code));
                    logPO.setEventMsg(msg);
                    logPO.setEventContent(messageBuilder.toString());
                    logPO.setRespData(JsonUtils.toJson(apiExp.getReceiveData()));
                } else {
                    StringBuilder sb = StringUtils.createBuilder(e).append("\n");
                    StackTraceElement[] stackTrace = e.getStackTrace();
                    for (StackTraceElement stackTraceElement : stackTrace) {
                        sb.append("\t" + stackTraceElement).append("\n");
                    }

                    logPO.setEventContent(sb.toString());
                    logPO.setEventCode("500");
                    logPO.setEventMsg("请求处理异常");
                }
            } else {
                //  获取环境配置
                logPO.setEventCode(ApiEnum.API_SIGN_SUC.getCode().toString());
                logPO.setEventMsg(ApiEnum.API_SIGN_SUC.getMsg());
                logPO.setRespData(JsonUtils.toJson(jsonResult));
            }

            logPO.setReqUrl(ServletUtils.getRequest().getRequestURI());
            logPO.setReqId(ClassConvertUtils.toLong(ApiContext.REQ_ID.get()));
            if (ValidParam.isNotNull(requestVO)) {
                logPO.setReqData(requestVO.getBody());
                logPO.setReqHeader(JsonUtils.toJson(requestVO.getHeader().getHeaders()));
            }

            //  请求参数日志记录
            apiLogAspectHandler.handlerRequestParameter(logPO);
            //  清理并发异常日志
            apiLogAspectHandler.clearConcurrentError(logPO);

            // 设置消耗时间
            logPO.setTaskTime(System.currentTimeMillis() - ApiContext.TIME_TIME.get());

            // 保存数据库
            apiLogExecute.execute(logPO);
        } finally {
            ApiLogAspect.removeApiContext();
        }
    }

}
